home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / mgr / sparcmgr / demo2.zoo / demo / ex / ex_vput.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-04-24  |  30.1 KB  |  1,369 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.  The Berkeley software License Agreement
  4.  * specifies the terms and conditions for redistribution.
  5.  */
  6.  
  7. #ifndef lint
  8. static char *sccsid = "@(#)ex_vput.c    7.5 (Berkeley) 3/9/87; 1.2 (Bellcore)    87/04/24";
  9. #endif not lint
  10.  
  11. #include "ex.h"
  12. #include "ex_tty.h"
  13. #include "ex_vis.h"
  14.  
  15. /*
  16.  * Deal with the screen, clearing, cursor positioning, putting characters
  17.  * into the screen image, and deleting characters.
  18.  * Really hard stuff here is utilizing insert character operations
  19.  * on intelligent terminals which differs widely from terminal to terminal.
  20.  */
  21. vclear()
  22. {
  23.  
  24. #ifdef ADEBUG
  25.     if (trace)
  26.         tfixnl(), fprintf(trace, "------\nvclear\n");
  27. #endif
  28.     tputs(CL, LINES, putch);
  29.     destcol = 0;
  30.     outcol = 0;
  31.     destline = 0;
  32.     outline = 0;
  33.     if (inopen)
  34.         vclrbyte(vtube0, WCOLS * (WECHO - ex_ZERO + 1));
  35. }
  36.  
  37. /*
  38.  * Clear memory.
  39.  */
  40. vclrbyte(cp, i)
  41.     register char *cp;
  42.     register int i;
  43. {
  44.  
  45.     if (i > 0)
  46.         do
  47.             *cp++ = 0;
  48.         while (--i != 0);
  49. }
  50.  
  51. /*
  52.  * Clear a physical display line, high level.
  53.  */
  54. vclrlin(l, tp)
  55.     int l;
  56.     line *tp;
  57. {
  58.  
  59.     vigoto(l, 0);
  60.     if ((hold & HOLDAT) == 0)
  61.         ex_putchar(tp > dol ? ((UPPERCASE || HZ) ? '^' : '~') : '@');
  62.     if (state == HARDOPEN)
  63.         sethard();
  64.     vclreol();
  65. }
  66.  
  67. /*
  68.  * Clear to the end of the current physical line
  69.  */
  70. vclreol()
  71. {
  72.     register int i, j;
  73.     register char *tp;
  74.  
  75.     if (destcol == WCOLS)
  76.         return;
  77.     destline += destcol / WCOLS;
  78.     destcol %= WCOLS;
  79.     if (destline < 0 || destline > WECHO)
  80.         error("Internal error: vclreol");
  81.     i = WCOLS - destcol;
  82.     tp = vtube[destline] + destcol;
  83.     if (CE) {
  84.         if (IN && *tp || !ateopr()) {
  85.             vcsync();
  86.             vputp(CE, 1);
  87.         }
  88.         vclrbyte(tp, i);
  89.         return;
  90.     }
  91.     if (*tp == 0)
  92.         return;
  93.     while (i > 0 && (j = *tp & (QUOTE|TRIM))) {
  94.         if (j != ' ' && (j & QUOTE) == 0) {
  95.             destcol = WCOLS - i;
  96.             vputchar(' ');
  97.         }
  98.         --i, *tp++ = 0;
  99.     }
  100. }
  101.  
  102. /*
  103.  * Clear the echo line.
  104.  * If didphys then its been cleared physically (as
  105.  * a side effect of a clear to end of display, e.g.)
  106.  * so just do it logically.
  107.  * If work here is being held off, just remember, in
  108.  * heldech, if work needs to be done, don't do anything.
  109.  */
  110. vclrech(didphys)
  111.     bool didphys;
  112. {
  113.  
  114.     if (Peek_key == ATTN)
  115.         return;
  116.     if (hold & HOLDECH) {
  117.         heldech = !didphys;
  118.         return;
  119.     }
  120.     if (!didphys && (CD || CE)) {
  121.         splitw++;
  122.         /*
  123.          * If display is retained below, then MUST use CD or CE
  124.          * since we don't really know whats out there.
  125.          * Vigoto might decide (incorrectly) to do nothing.
  126.          */
  127.         if (DB) {
  128.             vgoto(WECHO, 0);
  129.             vputp(CD ? CD : CE, 1);
  130.         } else {
  131.             if (XT) {
  132.                 /*
  133.                  * This code basically handles the t1061
  134.                  * where positioning at (0, 0) won't work
  135.                  * because the terminal won't let you put
  136.                  * the cursor on it's magic cookie.
  137.                  *
  138.                  * Should probably be XS above, or even a
  139.                  * new X? glitch, but right now t1061 is the
  140.                  * only terminal with XT.
  141.                  */
  142.                 vgoto(WECHO, 0);
  143.                 vputp(DL, 1);
  144.             } else {
  145.                 vigoto(WECHO, 0);
  146.                 vclreol();
  147.             }
  148.         }
  149.         splitw = 0;
  150.         didphys = 1;
  151.     }
  152.     if (didphys)
  153.         vclrbyte(vtube[WECHO], WCOLS);
  154.     heldech = 0;
  155. }
  156.  
  157. /*
  158.  * Fix the echo area for use, setting
  159.  * the state variable splitw so we wont rollup
  160.  * when we move the cursor there.
  161.  */
  162. fixech()
  163. {
  164.  
  165.     splitw++;
  166.     if (state != VISUAL && state != CRTOPEN) {
  167.         vclean();
  168.         vcnt = 0;
  169.     }
  170.     vgoto(WECHO, 0); flusho();
  171. }
  172.  
  173. /*
  174.  * Put the cursor ``before'' cp.
  175.  */
  176. vcursbef(cp)
  177.     register char *cp;
  178. {
  179.  
  180.     if (cp <= linebuf)
  181.         vgotoCL(value(NUMBER) << 3);
  182.     else
  183.         vgotoCL(column(cp - 1) - 1);
  184. }
  185.  
  186. /*
  187.  * Put the cursor ``at'' cp.
  188.  */
  189. vcursat(cp)
  190.     register char *cp;
  191. {
  192.  
  193.     if (cp <= linebuf && linebuf[0] == 0)
  194.         vgotoCL(value(NUMBER) << 3);
  195.     else
  196.         vgotoCL(column(cp - 1));
  197. }
  198.  
  199. /*
  200.  * Put the cursor ``after'' cp.
  201.  */
  202. vcursaft(cp)
  203.     register char *cp;
  204. {
  205.  
  206.     vgotoCL(column(cp));
  207. }
  208.  
  209. /*
  210.  * Fix the cursor to be positioned in the correct place
  211.  * to accept a command.
  212.  */
  213. vfixcurs()
  214. {
  215.  
  216.     vsetcurs(cursor);
  217. }
  218.  
  219. /*
  220.  * Compute the column position implied by the cursor at ``nc'',
  221.  * and move the cursor there.
  222.  */
  223. vsetcurs(nc)
  224.     register char *nc;
  225. {
  226.     register int col;
  227.  
  228.     col = column(nc);
  229.     if (linebuf[0])
  230.         col--;
  231.     vgotoCL(col);
  232.     cursor = nc;
  233. }
  234.  
  235. /*
  236.  * Move the cursor invisibly, i.e. only remember to do it.
  237.  */
  238. vigoto(y, x)
  239.     int y, x;
  240. {
  241.  
  242.     destline = y;
  243.     destcol = x;
  244. }
  245.  
  246. /*
  247.  * Move the cursor to the position implied by any previous
  248.  * vigoto (or low level hacking with destcol/destline as in readecho).
  249.  */
  250. vcsync()
  251. {
  252.  
  253.     vgoto(destline, destcol);
  254. }
  255.  
  256. /*
  257.  * Goto column x of the current line.
  258.  */
  259. vgotoCL(x)
  260.     register int x;
  261. {
  262.  
  263.     if (splitw)
  264.         vgoto(WECHO, x);
  265.     else
  266.         vgoto(LINE(vcline), x);
  267. }
  268.  
  269. /*
  270.  * Invisible goto column x of current line.
  271.  */
  272. vigotoCL(x)
  273.     register int x;
  274. {
  275.  
  276.     if (splitw)
  277.         vigoto(WECHO, x);
  278.     else
  279.         vigoto(LINE(vcline), x);
  280. }
  281.  
  282. /*
  283.  * Move cursor to line y, column x, handling wraparound and scrolling.
  284.  */
  285. vgoto(y, x)
  286.     register int y, x;
  287. {
  288.     register char *tp;
  289.     register int c;
  290.  
  291.     /*
  292.      * Fold the possibly too large value of x.
  293.      */
  294.     if (x >= WCOLS) {
  295.         y += x / WCOLS;
  296.         x %= WCOLS;
  297.     }
  298.     if (y < 0)
  299.         error("Internal error: vgoto");
  300.     if (outcol >= WCOLS) {
  301.         if (AM) {
  302.             outline += outcol / WCOLS;
  303.             outcol %= WCOLS;
  304.         } else
  305.             outcol = WCOLS - 1;
  306.     }
  307.  
  308.     /*
  309.      * In a hardcopy or glass crt open, print the stuff
  310.      * implied by a motion, or backspace.
  311.      */
  312.     if (state == HARDOPEN || state == ONEOPEN) {
  313.         if (y != outline)
  314.             error("Line too long for open");
  315.         if (x + 1 < outcol - x || (outcol > x && !BS))
  316.             destcol = 0, fgoto();
  317.         tp = vtube[WBOT] + outcol;
  318.         while (outcol != x)
  319.             if (outcol < x) {
  320.                 if (*tp == 0)
  321.                     *tp = ' ';
  322.                 c = *tp++ & TRIM;
  323.                 vputc(c && (!OS || EO) ? c : ' '), outcol++;
  324.             } else {
  325.                 if (BC)
  326.                     vputp(BC, 0);
  327.                 else
  328.                     vputc('\b');
  329.                 outcol--;
  330.             }
  331.         destcol = outcol = x;
  332.         destline = outline;
  333.         return;
  334.     }
  335.  
  336.     /*
  337.      * If the destination position implies a scroll, do it.
  338.      */
  339.     destline = y;
  340.     if (destline > WBOT && (!splitw || destline > WECHO)) {
  341.         endim();
  342.         vrollup(destline);
  343.     }
  344.  
  345.     /*
  346.      * If there really is a motion involved, do it.
  347.      * The check here is an optimization based on profiling.
  348.      */
  349.     destcol = x;
  350.     if ((destline - outline) * WCOLS != destcol - outcol) {
  351.         if (!MI)
  352.             endim();
  353.         fgoto();
  354.     }
  355. }
  356.  
  357. /*
  358.  * This is the hardest code in the editor, and deals with insert modes
  359.  * on different kinds of intelligent terminals.  The complexity is due
  360.  * to the cross product of three factors:
  361.  *
  362.  *    1. Lines may display as more than one segment on the screen.
  363.  *    2. There are 2 kinds of intelligent terminal insert modes.
  364.  *    3. Tabs squash when you insert characters in front of them,
  365.  *       in a way in which current intelligent terminals don't handle.
  366.  *
  367.  * The two kinds of terminals are typified by the DM2500 or HP2645 for
  368.  * one and the CONCEPT-100 or the FOX for the other.
  369.  *
  370.  * The first (HP2645) kind has an insert mode where the characters
  371.  * fall off the end of the line and the screen is shifted rigidly
  372.  * no matter how the display came about.
  373.  *
  374.  * The second (CONCEPT-100) kind comes from terminals which are designed
  375.  * for forms editing and which distinguish between blanks and ``spaces''
  376.  * on the screen, spaces being like blank, but never having had
  377.  * and data typed into that screen position (since, e.g. a clear operation
  378.  * like clear screen).  On these terminals, when you insert a character,
  379.  * the characters from where you are to the end of the screen shift
  380.  * over till a ``space'' is found, and the null character there gets
  381.  * eaten up.
  382.  *
  383.  *
  384.  * The code here considers the line as consisting of several parts
  385.  * the first part is the ``doomed'' part, i.e. a part of the line
  386.  * which is being typed over.  Next comes some text up to the first
  387.  * following tab.  The tab is the next segment of the line, and finally
  388.  * text after the tab.
  389.  *
  390.  * We have to consider each of these segments and the effect of the
  391.  * insertion of a character on them.  On terminals like HP2645's we
  392.  * must simulate a multi-line insert mode using the primitive one
  393.  * line insert mode.  If we are inserting in front of a tab, we have
  394.  * to either delete characters from the tab or insert white space
  395.  * (when the tab reaches a new spot where it gets larger) before we
  396.  * insert the new character.
  397.  *
  398.  * On a terminal like a CONCEPT our strategy is to make all
  399.  * blanks be displayed, while trying to keep the screen having ``spaces''
  400.  * for portions of tabs.  In this way the terminal hardward does some
  401.  * of the hacking for compression of tabs, although this tends to
  402.  * disappear as you work on the line and spaces change into blanks.
  403.  *
  404.  * There are a number of boundary conditions (like typing just before
  405.  * the first following tab) where we can avoid a lot of work.  Most
  406.  * of them have to be dealt with explicitly because performance is
  407.  * much, much worse if we don't.
  408.  *
  409.  * A final thing which is hacked here is two flavors of insert mode.
  410.  * Datamedia's do this by an insert mode which you enter and leave
  411.  * and by having normal motion character operate differently in this
  412.  * mode, notably by having a newline insert a line on the screen in
  413.  * this mode.  This generally means it is unsafe to move around
  414.  * the screen ignoring the fact that we are in this mode.
  415.  * This is possible on some terminals, and wins big (e.g. HP), so
  416.  * we encode this as a ``can move in insert capability'' mi,
  417.  * and terminals which have it can do insert mode with much less
  418.  * work when tabs are present following the cursor on the current line.
  419.  */
  420.  
  421. /*
  422.  * Routine to expand a tab, calling the normal Outchar routine
  423.  * to put out each implied character.  Note that we call outchar
  424.  * with a QUOTE.  We use QUOTE internally to represent a position
  425.  * which is part of the expansion of a tab.
  426.  */
  427. vgotab()
  428. {
  429.     register int i = tabcol(destcol, value(TABSTOP)) - destcol;
  430.  
  431.     do
  432.         (*Outchar)(QUOTE);
  433.     while (--i);
  434. }
  435.  
  436. /*
  437.  * Variables for insert mode.
  438.  */
  439. int    linend;            /* The column position of end of line */
  440. int    tabstart;        /* Column of start of first following tab */
  441. int    tabend;            /* Column of end of following tabs */
  442. int    tabsize;        /* Size of the following tabs */
  443. int    tabslack;        /* Number of ``spaces'' in following tabs */
  444. int    inssiz;            /* Number of characters to be inserted */
  445. int    inscol;            /* Column where insertion is taking place */
  446. int    shft;            /* Amount tab expansion shifted rest of line */
  447. int    slakused;        /* This much of tabslack will be used up */
  448.  
  449. /*
  450.  * This routine MUST be called before insert mode is run,
  451.  * and brings all segments of the current line to the top
  452.  * of the screen image buffer so it is easier for us to
  453.  * maniuplate them.
  454.  */
  455. vprepins()
  456. {
  457.     register int i;
  458.     register char *cp = vtube0;
  459.  
  460.     for (i = 0; i < DEPTH(vcline); i++) {
  461.         vmaktop(LINE(vcline) + i, cp);
  462.         cp += WCOLS;
  463.     }
  464. }
  465.  
  466. vmaktop(p, cp)
  467.     register int p;
  468.     char *cp;
  469. {
  470.     register int i;
  471.     char temp[TUBECOLS];
  472.  
  473.     if (p < 0 || vtube[p] == cp)
  474.         return;
  475.     for (i = ex_ZERO; i <= WECHO; i++)
  476.         if (vtube[i] == cp) {
  477.             copy(temp, vtube[i], WCOLS);
  478.             copy(vtube[i], vtube[p], WCOLS);
  479.             copy(vtube[p], temp, WCOLS);
  480.             vtube[i] = vtube[p];
  481.             vtube[p] = cp;
  482.             return;
  483.         }
  484.     error("Line too long");
  485. }
  486.  
  487. /*
  488.  * Insert character c at current cursor position.
  489.  * Multi-character inserts occur only as a result
  490.  * of expansion of tabs (i.e. inssize == 1 except
  491.  * for tabs) and code assumes this in several place
  492.  * to make life simpler.
  493.  */
  494. vinschar(c)
  495.     int c;        /* mjm: char --> int */
  496. {
  497.     register int i;
  498.     register char *tp;
  499.  
  500.     if ((!IM || !EI) && ((hold & HOLDQIK) || !value(REDRAW) || value(SLOWOPEN))) {
  501.         /*
  502.          * Don't want to try to use terminal
  503.          * insert mode, or to try to fake it.
  504.          * Just put the character out; the screen
  505.          * will probably be wrong but we will fix it later.
  506.          */
  507.         if (c == '\t') {
  508.             vgotab();
  509.             return;
  510.         }
  511.         vputchar(c);
  512.         if (DEPTH(vcline) * WCOLS + !value(REDRAW) >
  513.             (destline - LINE(vcline)) * WCOLS + destcol)
  514.             return;
  515.         /*
  516.          * The next line is about to be clobbered
  517.          * make space for another segment of this line
  518.          * (on an intelligent terminal) or just remember
  519.          * that next line was clobbered (on a dumb one
  520.          * if we don't care to redraw the tail.
  521.          */
  522.         if (AL) {
  523.             vnpins(0);
  524.         } else {
  525.             c = LINE(vcline) + DEPTH(vcline);
  526.             if (c < LINE(vcline + 1) || c > WBOT)
  527.                 return;
  528.             i = destcol;
  529.             vinslin(c, 1, vcline);
  530.             DEPTH(vcline)++;
  531.             vigoto(c, i);
  532.             vprepins();
  533.         }
  534.         return;
  535.     }
  536.     /*
  537.      * Compute the number of positions in the line image of the
  538.      * current line.  This is done from the physical image
  539.      * since that is faster.  Note that we have no memory
  540.      * from insertion to insertion so that routines which use
  541.      * us don't have to worry about moving the cursor around.
  542.      */
  543.     if (*vtube0 == 0)
  544.         linend = 0;
  545.     else {
  546.         /*
  547.          * Search backwards for a non-null character
  548.          * from the end of the displayed line.
  549.          */
  550.         i = WCOLS * DEPTH(vcline);
  551.         if (i == 0)
  552.             i = WCOLS;
  553.         tp = vtube0 + i;
  554.         while (*--tp == 0)
  555.             if (--i == 0)
  556.                 break;
  557.         linend = i;
  558.     }
  559.  
  560.     /*
  561.      * We insert at a position based on the physical location
  562.      * of the output cursor.
  563.      */
  564.     inscol = destcol + (destline - LINE(vcline)) * WCOLS;
  565.     if (c == '\t') {
  566.         /*
  567.          * Characters inserted from a tab must be
  568.          * remembered as being part of a tab, but we can't
  569.          * use QUOTE here since we really need to print blanks.
  570.          * QUOTE|' ' is the representation of this.
  571.          */
  572.         inssiz = tabcol(inscol, value(TABSTOP)) - inscol;
  573.         c = ' ' | QUOTE;
  574.     } else
  575.         inssiz = 1;
  576.  
  577.     /*
  578.      * If the text to be inserted is less than the number
  579.      * of doomed positions, then we don't need insert mode,
  580.      * rather we can just typeover.
  581.      */
  582.     if (inssiz <= doomed) {
  583.         endim();
  584.         if (inscol != linend)
  585.             doomed -= inssiz;
  586.         do
  587.             vputchar(c);
  588.         while (--inssiz);
  589.         return;
  590.     }
  591.  
  592.     /*
  593.      * Have to really do some insertion, thus
  594.      * stake out the bounds of the first following
  595.      * group of tabs, computing starting position,
  596.      * ending position, and the number of ``spaces'' therein
  597.      * so we can tell how much it will squish.
  598.      */
  599.     tp = vtube0 + inscol;
  600.     for (i = inscol; i < linend; i++)
  601.         if (*tp++ & QUOTE) {
  602.             --tp;
  603.             break;
  604.         }
  605.     tabstart = tabend = i;
  606.     tabslack = 0;
  607.     while (tabend < linend) {
  608.         i = *tp++;
  609.         if ((i & QUOTE) == 0)
  610.             break;
  611.         if ((i & TRIM) == 0)
  612.             tabslack++;
  613.         tabsize++;
  614.         tabend++;
  615.     }
  616.     tabsize = tabend - tabstart;
  617.  
  618.     /*
  619.      * For HP's and DM's, e.g. tabslack has no meaning.
  620.      */
  621.     if (!IN)
  622.         tabslack = 0;
  623. #ifdef IDEBUG
  624.     if (trace) {
  625.         fprintf(trace, "inscol %d, inssiz %d, tabstart %d, ",
  626.             inscol, inssiz, tabstart);
  627.         fprintf(trace, "tabend %d, tabslack %d, linend %d\n",
  628.             tabend, tabslack, linend);
  629.     }
  630. #endif
  631.  
  632.     /*
  633.      * The real work begins.
  634.      */
  635.     slakused = 0;
  636.     shft = 0;
  637.     if (tabsize) {
  638.         /*
  639.          * There are tabs on this line.
  640.          * If they need to expand, then the rest of the line
  641.          * will have to be shifted over.  In this case,
  642.          * we will need to make sure there are no ``spaces''
  643.          * in the rest of the line (on e.g. CONCEPT-100)
  644.          * and then grab another segment on the screen if this
  645.          * line is now deeper.  We then do the shift
  646.          * implied by the insertion.
  647.          */
  648.         if (inssiz >= doomed + tabcol(tabstart, value(TABSTOP)) - tabstart) {
  649.             if (IN)
  650.                 vrigid();
  651.             vneedpos(value(TABSTOP));
  652.             vishft();
  653.         }
  654.     } else if (inssiz > doomed)
  655.         /*
  656.          * No tabs, but line may still get deeper.
  657.          */
  658.         vneedpos(inssiz - doomed);
  659.     /*
  660.      * Now put in the inserted characters.
  661.      */
  662.     viin(c);
  663.  
  664.     /*
  665.      * Now put the cursor in its final resting place.
  666.      */
  667.     destline = LINE(vcline);
  668.     destcol = inscol + inssiz;
  669.     vcsync();
  670. }
  671.  
  672. /*
  673.  * Rigidify the rest of the line after the first
  674.  * group of following tabs, typing blanks over ``spaces''.
  675.  */
  676. vrigid()
  677. {
  678.     register int col;
  679.     register char *tp = vtube0 + tabend;
  680.  
  681.     for (col = tabend; col < linend; col++)
  682.         if ((*tp++ & TRIM) == 0) {
  683.             endim();
  684.             vgotoCL(col);
  685.             vputchar(' ' | QUOTE);
  686.         }
  687. }
  688.  
  689. /*
  690.  * We need cnt more positions on this line.
  691.  * Open up new space on the screen (this may in fact be a
  692.  * screen rollup).
  693.  *
  694.  * On a dumb terminal we may infact redisplay the rest of the
  695.  * screen here brute force to keep it pretty.
  696.  */
  697. vneedpos(cnt)
  698.     int cnt;
  699. {
  700.     register int d = DEPTH(vcline);
  701.     register int rmdr = d * WCOLS - linend;
  702.  
  703.     if (cnt <= rmdr - IN)
  704.         return;
  705.     endim();
  706.     vnpins(1);
  707. }
  708.  
  709. vnpins(dosync)
  710.     int dosync;
  711. {
  712.     register int d = DEPTH(vcline);
  713.     register int e;
  714.  
  715.     e = LINE(vcline) + DEPTH(vcline);
  716.     if (e < LINE(vcline + 1)) {
  717.         vigoto(e, 0);
  718.         vclreol();
  719.         return;
  720.     }
  721.     DEPTH(vcline)++;
  722.     if (e < WECHO) {
  723.         e = vglitchup(vcline, d);
  724.         vigoto(e, 0); vclreol();
  725.         if (dosync) {
  726.             int (*Ooutchar)() = Outchar;
  727.             Outchar = vputchar;
  728.             vsync(e + 1);
  729.             Outchar = Ooutchar;
  730.         }
  731.     } else {
  732.         vup1();
  733.         vigoto(WBOT, 0);
  734.         vclreol();
  735.     }
  736.     vprepins();
  737. }
  738.  
  739. /*
  740.  * Do the shift of the next tabstop implied by
  741.  * insertion so it expands.
  742.  */
  743. vishft()
  744. {
  745.     int tshft = 0;
  746.     int j;
  747.     register int i;
  748.     register char *tp = vtube0;
  749.     register char *up;
  750.     short oldhold = hold;
  751.  
  752.     shft = value(TABSTOP);
  753.     hold |= HOLDPUPD;
  754.     if (!IM && !EI) {
  755.         /*
  756.          * Dumb terminals are easy, we just have
  757.          * to retype the text.
  758.          */
  759.         vigotoCL(tabend + shft);
  760.         up = tp + tabend;
  761.         for (i = tabend; i < linend; i++)
  762.             vputchar(*up++);
  763.     } else if (IN) {
  764.         /*
  765.          * CONCEPT-like terminals do most of the work for us,
  766.          * we don't have to muck with simulation of multi-line
  767.          * insert mode.  Some of the shifting may come for free
  768.          * also if the tabs don't have enough slack to take up
  769.          * all the inserted characters.
  770.          */
  771.         i = shft;
  772.         slakused = inssiz - doomed;
  773.         if (slakused > tabslack) {
  774.             i -= slakused - tabslack;
  775.             slakused -= tabslack;
  776.         }
  777.         if (i > 0 && tabend != linend) {
  778.             tshft = i;
  779.             vgotoCL(tabend);
  780.             goim();
  781.             do
  782.                 vputchar(' ' | QUOTE);
  783.             while (--i);
  784.         }
  785.     } else {
  786.         /*
  787.          * HP and Datamedia type terminals have to have multi-line
  788.          * insert faked.  Hack each segment after where we are
  789.          * (going backwards to where we are.)  We then can
  790.          * hack the segment where the end of the first following
  791.          * tab group is.
  792.          */
  793.         for (j = DEPTH(vcline) - 1; j > (tabend + shft) / WCOLS; j--) {
  794.             vgotoCL(j * WCOLS);
  795.             goim();
  796.             up = tp + j * WCOLS - shft;
  797.             i = shft;
  798.             do {
  799.                 if (*up)
  800.                     vputchar(*up++);
  801.                 else
  802.                     break;
  803.             } while (--i);
  804.         }
  805.         vigotoCL(tabstart);
  806.         i = shft - (inssiz - doomed);
  807.         if (i > 0) {
  808.             tabslack = inssiz - doomed;
  809.             vcsync();
  810.             goim();
  811.             do
  812.                 vputchar(' ');
  813.             while (--i);
  814.         }
  815.     }
  816.     /*
  817.      * Now do the data moving in the internal screen
  818.      * image which is common to all three cases.
  819.      */
  820.     tp += linend;
  821.     up = tp + shft;
  822.     i = linend - tabend;
  823.     if (i > 0)
  824.         do
  825.             *--up = *--tp;
  826.         while (--i);
  827.     if (IN && tshft) {
  828.         i = tshft;
  829.         do
  830.             *--up = ' ' | QUOTE;
  831.         while (--i);
  832.     }
  833.     hold = oldhold;
  834. }
  835.  
  836. /*
  837.  * Now do the insert of the characters (finally).
  838.  */
  839. viin(c)
  840.     int c;        /* mjm: char --> int */
  841. {
  842.     register char *tp, *up;
  843.     register int i, j;
  844.     register bool noim = 0;
  845.     int remdoom;
  846.     short oldhold = hold;
  847.  
  848.     hold |= HOLDPUPD;
  849.     if (tabsize && (IM && EI) && inssiz - doomed > tabslack)
  850.         /*
  851.          * There is a tab out there which will be affected
  852.          * by the insertion since there aren't enough doomed
  853.          * characters to take up all the insertion and we do
  854.          * have insert mode capability.
  855.          */
  856.         if (inscol + doomed == tabstart) {
  857.             /*
  858.              * The end of the doomed characters sits right at the
  859.              * start of the tabs, then we don't need to use insert
  860.              * mode; unless the tab has already been expanded
  861.              * in which case we MUST use insert mode.
  862.              */
  863.             slakused = 0;
  864.             noim = !shft;
  865.         } else {
  866.             /*
  867.              * The last really special case to handle is case
  868.              * where the tab is just sitting there and doesn't
  869.              * have enough slack to let the insertion take
  870.              * place without shifting the rest of the line
  871.              * over.  In this case we have to go out and
  872.              * delete some characters of the tab before we start
  873.              * or the answer will be wrong, as the rest of the
  874.              * line will have been shifted.  This code means
  875.              * that terminals with only insert chracter (no
  876.              * delete character) won't work correctly.
  877.              */
  878.             i = inssiz - doomed - tabslack - slakused;
  879.             i %= value(TABSTOP);
  880.             if (i > 0) {
  881.                 vgotoCL(tabstart);
  882.                 godm();
  883.                 for (i = inssiz - doomed - tabslack; i > 0; i--)
  884.                     vputp(DC, DEPTH(vcline));
  885.                 enddm();
  886.             }
  887.         }
  888.  
  889.     /* 
  890.      * Now put out the characters of the actual insertion.
  891.      */
  892.     vigotoCL(inscol);
  893.     remdoom = doomed;
  894.     for (i = inssiz; i > 0; i--) {
  895.         if (remdoom > 0) {
  896.             remdoom--;
  897.             endim();
  898.         } else if (noim)
  899.             endim();
  900.         else if (IM && EI) {
  901.             vcsync();
  902.             goim();
  903.         }
  904.         vputchar(c);
  905.     }
  906.  
  907.     if (!IM || !EI) {
  908.         /*
  909.          * We are a dumb terminal; brute force update
  910.          * the rest of the line; this is very much an n^^2 process,
  911.          * and totally unreasonable at low speed.
  912.          *
  913.          * You asked for it, you get it.
  914.          */
  915.         tp = vtube0 + inscol + doomed;
  916.         for (i = inscol + doomed; i < tabstart; i++)
  917.             vputchar(*tp++);
  918.         hold = oldhold;
  919.         vigotoCL(tabstart + inssiz - doomed);
  920.         for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
  921.             vputchar(' ' | QUOTE);
  922.     } else {
  923.         if (!IN) {
  924.             /*
  925.              * On terminals without multi-line
  926.              * insert in the hardware, we must go fix the segments
  927.              * between the inserted text and the following
  928.              * tabs, if they are on different lines.
  929.              *
  930.              * Aaargh.
  931.              */
  932.             tp = vtube0;
  933.             for (j = (inscol + inssiz - 1) / WCOLS + 1;
  934.                 j <= (tabstart + inssiz - doomed - 1) / WCOLS; j++) {
  935.                 vgotoCL(j * WCOLS);
  936.                 i = inssiz - doomed;
  937.                 up = tp + j * WCOLS - i;
  938.                 goim();
  939.                 do
  940.                     vputchar(*up++);
  941.                 while (--i && *up);
  942.             }
  943.         } else {
  944.             /*
  945.              * On terminals with multi line inserts,
  946.              * life is simpler, just reflect eating of
  947.              * the slack.
  948.              */
  949.             tp = vtube0 + tabend;
  950.             for (i = tabsize - (inssiz - doomed); i >= 0; i--) {
  951.                 if ((*--tp & (QUOTE|TRIM)) == QUOTE) {
  952.                     --tabslack;
  953.                     if (tabslack >= slakused)
  954.                         continue;
  955.                 }
  956.                 *tp = ' ' | QUOTE;
  957.             }
  958.         }
  959.         /*
  960.          * Blank out the shifted positions to be tab positions.
  961.          */
  962.         if (shft) {
  963.             tp = vtube0 + tabend + shft;
  964.             for (i = tabsize - (inssiz - doomed) + shft; i > 0; i--)
  965.                 if ((*--tp & QUOTE) == 0)
  966.                     *tp = ' ' | QUOTE;
  967.         }
  968.     }
  969.  
  970.     /*
  971.      * Finally, complete the screen image update
  972.      * to reflect the insertion.
  973.      */
  974.     hold = oldhold;
  975.     tp = vtube0 + tabstart; up = tp + inssiz - doomed;
  976.     for (i = tabstart; i > inscol + doomed; i--)
  977.         *--up = *--tp;
  978.     for (i = inssiz; i > 0; i--)
  979.         *--up = c;
  980.     doomed = 0;
  981. }
  982.  
  983. /*
  984.  * Go into ``delete mode''.  If the
  985.  * sequence which goes into delete mode
  986.  * is the same as that which goes into insert
  987.  * mode, then we are in delete mode already.
  988.  */
  989. godm()
  990. {
  991.  
  992.     if (insmode) {
  993.         if (eq(DM, IM))
  994.             return;
  995.         endim();
  996.     }
  997.     vputp(DM, 0);
  998. }
  999.  
  1000. /*
  1001.  * If we are coming out of delete mode, but
  1002.  * delete and insert mode end with the same sequence,
  1003.  * it wins to pretend we are now in insert mode,
  1004.  * since we will likely want to be there again soon
  1005.  * if we just moved over to delete space from part of
  1006.  * a tab (above).
  1007.  */
  1008. enddm()
  1009. {
  1010.  
  1011.     if (eq(DM, IM)) {
  1012.         insmode = 1;
  1013.         return;
  1014.     }
  1015.     vputp(ED, 0);
  1016. }
  1017.  
  1018. /*
  1019.  * In and out of insert mode.
  1020.  * Note that the code here demands that there be
  1021.  * a string for insert mode (the null string) even
  1022.  * if the terminal does all insertions a single character
  1023.  * at a time, since it branches based on whether IM is null.
  1024.  */
  1025. goim()
  1026. {
  1027.  
  1028.     if (!insmode)
  1029.         vputp(IM, 0);
  1030.     insmode = 1;
  1031. }
  1032.  
  1033. endim()
  1034. {
  1035.  
  1036.     if (insmode) {
  1037.         vputp(EI, 0);
  1038.         insmode = 0;
  1039.     }
  1040. }
  1041.  
  1042. /*
  1043.  * Put the character c on the screen at the current cursor position.
  1044.  * This routine handles wraparound and scrolling and understands not
  1045.  * to roll when splitw is set, i.e. we are working in the echo area.
  1046.  * There is a bunch of hacking here dealing with the difference between
  1047.  * QUOTE, QUOTE|' ', and ' ' for CONCEPT-100 like terminals, and also
  1048.  * code to deal with terminals which overstrike, including CRT's where
  1049.  * you can erase overstrikes with some work.  CRT's which do underlining
  1050.  * implicitly which has to be erased (like CONCEPTS) are also handled.
  1051.  */
  1052. vputchar(c)
  1053.     register int c;
  1054. {
  1055.     register char *tp;
  1056.     register int d;
  1057.  
  1058.     c &= (QUOTE|TRIM);
  1059. #ifdef TRACE
  1060.     if (trace)
  1061.         tracec(c);
  1062. #endif
  1063.     /* Fix problem of >79 chars on echo line. */
  1064.     if (destcol >= WCOLS-1 && splitw && destline == WECHO)
  1065.         pofix();
  1066.     if (destcol >= WCOLS) {
  1067.         destline += destcol / WCOLS;
  1068.         destcol %= WCOLS;
  1069.     }
  1070.     if (destline > WBOT && (!splitw || destline > WECHO))
  1071.         vrollup(destline);
  1072.     tp = vtube[destline] + destcol;
  1073.     switch (c) {
  1074.  
  1075.     case '\t':
  1076.         vgotab();
  1077.         return;
  1078.  
  1079.     case ' ':
  1080.         /*
  1081.          * We can get away without printing a space in a number
  1082.          * of cases, but not always.  We get away with doing nothing
  1083.          * if we are not in insert mode, and not on a CONCEPT-100
  1084.          * like terminal, and either not in hardcopy open or in hardcopy
  1085.          * open on a terminal with no overstriking, provided,
  1086.          * in all cases, that nothing has ever been displayed
  1087.          * at this position.  Ugh.
  1088.          */
  1089.         if (!insmode && !IN && (state != HARDOPEN || OS) && (*tp&TRIM) == 0) {
  1090.             *tp = ' ';
  1091.             destcol++;
  1092.             return;
  1093.         }
  1094.         goto def;
  1095.  
  1096.     case QUOTE:
  1097.         if (insmode) {
  1098.             /*
  1099.              * When in insert mode, tabs have to expand
  1100.              * to real, printed blanks.
  1101.              */
  1102.             c = ' ' | QUOTE;
  1103.             goto def;
  1104.         }
  1105.         if (*tp == 0) {
  1106.             /*
  1107.              * A ``space''.
  1108.              */
  1109.             if ((hold & HOLDPUPD) == 0)
  1110.                 *tp = QUOTE;
  1111.             destcol++;
  1112.             return;
  1113.         }
  1114.         /*
  1115.          * A ``space'' ontop of a part of a tab.
  1116.          */
  1117.         if (*tp & QUOTE) {
  1118.             destcol++;
  1119.             return;
  1120.         }
  1121.         c = ' ' | QUOTE;
  1122.         /* fall into ... */
  1123.  
  1124. def:
  1125.     default:
  1126.         d = *tp & TRIM;
  1127.         /*
  1128.          * Now get away with doing nothing if the characters
  1129.          * are the same, provided we are not in insert mode
  1130.          * and if we are in hardopen, that the terminal has overstrike.
  1131.          */
  1132.         if (d == (c & TRIM) && !insmode && (state != HARDOPEN || OS)) {
  1133.             if ((hold & HOLDPUPD) == 0)
  1134.                 *tp = c;
  1135.             destcol++;
  1136.             return;
  1137.         }
  1138.         /*
  1139.          * Backwards looking optimization.
  1140.          * The low level cursor motion routines will use
  1141.          * a cursor motion right sequence to step 1 character
  1142.          * right.  On, e.g., a DM3025A this is 2 characters
  1143.          * and printing is noticeably slower at 300 baud.
  1144.          * Since the low level routines are not allowed to use
  1145.          * spaces for positioning, we discover the common
  1146.          * case of a single space here and force a space
  1147.          * to be printed.
  1148.          */
  1149.         if (destcol == outcol + 1 && tp[-1] == ' ' && outline == destline) {
  1150.             vputc(' ');
  1151.             outcol++;
  1152.         }
  1153.  
  1154.         /*
  1155.          * This is an inline expansion a call to vcsync() dictated
  1156.          * by high frequency in a profile.
  1157.          */
  1158.         if (outcol != destcol || outline != destline)
  1159.             vgoto(destline, destcol);
  1160.  
  1161.         /*
  1162.          * Deal with terminals which have overstrike.
  1163.          * We handle erasing general overstrikes, erasing
  1164.          * underlines on terminals (such as CONCEPTS) which
  1165.          * do underlining correctly automatically (e.g. on nroff
  1166.          * output), and remembering, in hardcopy mode,
  1167.          * that we have overstruct something.
  1168.          */
  1169.         if (!insmode && d && d != ' ' && d != (c & TRIM)) {
  1170.             if (EO && (OS || UL && (c == '_' || d == '_'))) {
  1171.                 vputc(' ');
  1172.                 outcol++, destcol++;
  1173.                 back1();
  1174.             } else
  1175.                 rubble = 1;
  1176.         }
  1177.  
  1178.         /*
  1179.          * Unless we are just bashing characters around for
  1180.          * inner working of insert mode, update the display.
  1181.          */
  1182.         if ((hold & HOLDPUPD) == 0)
  1183.             *tp = c;
  1184.  
  1185.         /*
  1186.          * In insert mode, put out the IC sequence, padded
  1187.          * based on the depth of the current line.
  1188.          * A terminal which had no real insert mode, rather
  1189.          * opening a character position at a time could do this.
  1190.          * Actually should use depth to end of current line
  1191.          * but this rarely matters.
  1192.          */
  1193.         if (insmode)
  1194.             vputp(IC, DEPTH(vcline));
  1195.         vputc(c & TRIM);
  1196.  
  1197.         /*
  1198.          * In insert mode, IP is a post insert pad.
  1199.          */
  1200.         if (insmode)
  1201.             vputp(IP, DEPTH(vcline));
  1202.         destcol++, outcol++;
  1203.  
  1204.         /*
  1205.          * CONCEPT braindamage in early models:  after a wraparound
  1206.          * the next newline is eaten.  It's hungry so we just
  1207.          * feed it now rather than worrying about it.
  1208.          * Fixed to use    return linefeed to work right
  1209.          * on vt100/tab132 as well as concept.
  1210.          */
  1211.         if (XN && outcol % WCOLS == 0) {
  1212.             vputc('\r');
  1213.             vputc('\n');
  1214.         }
  1215.     }
  1216. }
  1217.  
  1218. /*
  1219.  * Delete display positions stcol through endcol.
  1220.  * Amount of use of special terminal features here is limited.
  1221.  */
  1222. physdc(stcol, endcol)
  1223.     int stcol, endcol;
  1224. {
  1225.     register char *tp, *up;
  1226.     char *tpe;
  1227.     register int i;
  1228.     register int nc = endcol - stcol;
  1229.  
  1230. #ifdef IDEBUG
  1231.     if (trace)
  1232.         tfixnl(), fprintf(trace, "physdc(%d, %d)\n", stcol, endcol);
  1233. #endif
  1234.     if (!DC || nc <= 0)
  1235.         return;
  1236.     if (IN) {
  1237.         /*
  1238.          * CONCEPT-100 like terminal.
  1239.          * If there are any ``spaces'' in the material to be
  1240.          * deleted, then this is too hard, just retype.
  1241.          */
  1242.         vprepins();
  1243.         up = vtube0 + stcol;
  1244.         i = nc;
  1245.         do
  1246.             if ((*up++ & (QUOTE|TRIM)) == QUOTE)
  1247.                 return;
  1248.         while (--i);
  1249.         i = 2 * nc;
  1250.         do
  1251.             if (*up == 0 || (*up++ & QUOTE) == QUOTE)
  1252.                 return;
  1253.         while (--i);
  1254.         vgotoCL(stcol);
  1255.     } else {
  1256.         /*
  1257.          * HP like delete mode.
  1258.          * Compute how much text we are moving over by deleting.
  1259.          * If it appears to be faster to just retype
  1260.          * the line, do nothing and that will be done later.
  1261.          * We are assuming 2 output characters per deleted
  1262.          * characters and that clear to end of line is available.
  1263.          */
  1264.         i = stcol / WCOLS;
  1265.         if (i != endcol / WCOLS)
  1266.             return;
  1267.         i += LINE(vcline);
  1268.         stcol %= WCOLS;
  1269.         endcol %= WCOLS;
  1270.         up = vtube[i]; tp = up + endcol; tpe = up + WCOLS;
  1271.         while (tp < tpe && *tp)
  1272.             tp++;
  1273.         if (tp - (up + stcol) < 2 * nc)
  1274.             return;
  1275.         vgoto(i, stcol);
  1276.     }
  1277.  
  1278.     /*
  1279.      * Go into delete mode and do the actual delete.
  1280.      * Padding is on DC itself.
  1281.      */
  1282.     godm();
  1283.     for (i = nc; i > 0; i--)
  1284.         vputp(DC, DEPTH(vcline));
  1285.     vputp(ED, 0);
  1286.  
  1287.     /*
  1288.      * Straighten up.
  1289.      * With CONCEPT like terminals, characters are pulled left
  1290.      * from first following null.  HP like terminals shift rest of
  1291.      * this (single physical) line rigidly.
  1292.      */
  1293.     if (IN) {
  1294.         up = vtube0 + stcol;
  1295.         tp = vtube0 + endcol;
  1296.         while (i = *tp++) {
  1297.             if ((i & (QUOTE|TRIM)) == QUOTE)
  1298.                 break;
  1299.             *up++ = i;
  1300.         }
  1301.         do
  1302.             *up++ = i;
  1303.         while (--nc);
  1304.     } else {
  1305.         copy(up + stcol, up + endcol, WCOLS - endcol);
  1306.         vclrbyte(tpe - nc, nc);
  1307.     }
  1308. }
  1309.  
  1310. #ifdef TRACE
  1311. tfixnl()
  1312. {
  1313.  
  1314.     if (trubble || techoin)
  1315.         fprintf(trace, "\n");
  1316.     trubble = 0, techoin = 0;
  1317. }
  1318.  
  1319. tvliny()
  1320. {
  1321.     register int i;
  1322.  
  1323.     if (!trace)
  1324.         return;
  1325.     tfixnl();
  1326.     fprintf(trace, "vcnt = %d, vcline = %d, vliny = ", vcnt, vcline);
  1327.     for (i = 0; i <= vcnt; i++) {
  1328.         fprintf(trace, "%d", LINE(i));
  1329.         if (FLAGS(i) & VDIRT)
  1330.             fprintf(trace, "*");
  1331.         if (DEPTH(i) != 1)
  1332.             fprintf(trace, "<%d>", DEPTH(i));
  1333.         if (i < vcnt)
  1334.             fprintf(trace, " ");
  1335.     }
  1336.     fprintf(trace, "\n");
  1337. }
  1338.  
  1339. tracec(c)
  1340.     int c;        /* mjm: char --> int */
  1341. {
  1342.  
  1343.     if (!techoin)
  1344.         trubble = 1;
  1345.     if (c == ESCAPE)
  1346.         fprintf(trace, "$");
  1347.     else if (c & QUOTE)    /* mjm: for 3B (no sign extension) */
  1348.         fprintf(trace, "~%c", ctlof(c&TRIM));
  1349.     else if (c < ' ' || c == DELETE)
  1350.         fprintf(trace, "^%c", ctlof(c));
  1351.     else
  1352.         fprintf(trace, "%c", c);
  1353. }
  1354. #endif
  1355.  
  1356. /*
  1357.  * Put a character with possible tracing.
  1358.  */
  1359. vputch(c)
  1360.     int c;
  1361. {
  1362.  
  1363. #ifdef TRACE
  1364.     if (trace)
  1365.         tracec(c);
  1366. #endif
  1367.     vputc(c);
  1368. }
  1369.